
module ip_tx#(
    parameter                   SOURCE_IP = {8'd192,8'd192,8'd7,8'd11 } 
)(
    input                       i_clk                                   ,
    input                       i_rst                                   ,
    input                       i_set_source_ip_valid                   ,
    input   [31: 0]             i_set_source_ip                         ,
    input                       s_axis_tvalid                           ,
    input   [7 : 0]             s_axis_tdata                            ,
    output                      s_axis_tready                           ,
    input                       s_axis_tlast                            ,
    input   [55: 0]             s_axis_tuser                            ,
    output                      m_axis_tvalid                           ,
    output  [7 : 0]             m_axis_tdata                            ,
    input                       m_axis_tready                           ,
    output                      m_axis_tlast                            ,
    output  [64: 0]             m_axis_tuser                            
);

    logic   [31: 0]             set_source_ip                           ;
    logic   [7 : 0]             axis_tx_data                            ;
    logic                       axis_tx_last                            ;
    logic                       axis_tx_valid                           ;
    logic                       axis_tx_ready                           ;
    logic   [31: 0]             ip_tx_target_ip                         ;
    logic   [15: 0]             ip_pkg_len                              ;
    logic   [7 : 0]             ip_pkg_type                             ;
    logic                       tx_handshake                            ;
    logic                       fifo_rden                               ;
    logic   [7 : 0]             fifo_rddata                             ;
    logic                       fifo_almost_full                        ;
    logic                       fifo_full                               ;
    logic                       fifo_empty                              ;
    logic                       fifo_empty_delay                        ;
    logic   [64: 0]             ip_axis_tx_user                         ;
    logic                       ip_axis_tx_last                         ;
    logic                       ip_axis_tx_valid                        ;
    logic   [7 : 0]             ip_tx_data                              ;
    logic   [15: 0]             ip_tx_id                                ;
    logic   [31: 0]             check_sum                               ;
    logic   [15: 0]             lsm_cnt                                 ;

    // ila128_bit ila128_bit_IP(
    //     .clk    ( i_clk),
    //     .probe0 ( {
    //             s_axis_tvalid,
    //             s_axis_tdata ,
    //             s_axis_tready,
    //             s_axis_tlast ,
    //             s_axis_tuser[15:0],
    //             m_axis_tvalid,
    //             m_axis_tdata ,
    //             m_axis_tready,
    //             m_axis_tlast ,
    //             m_axis_tuser[15:0],
    //             ip_pkg_len      ,
    //             ip_pkg_type     ,
    //             fifo_rden       ,
    //             fifo_rddata     ,
    //             fifo_almost_full,
    //             fifo_full       ,
    //             fifo_empty      ,
    //             ip_tx_data      ,
    //             lsm_cnt         
    //               })

    // );


    assign s_axis_tready = axis_tx_ready;
    assign m_axis_tdata = ip_tx_data;
    assign m_axis_tlast = ip_axis_tx_last;
    assign m_axis_tvalid = ip_axis_tx_valid;
    assign m_axis_tuser = ip_axis_tx_user;

    always_ff@(posedge i_clk) begin
        if(i_rst) 
            set_source_ip <= SOURCE_IP;
        else if(i_set_source_ip_valid)
            set_source_ip <= i_set_source_ip;
        else    
            set_source_ip <= set_source_ip;
    end

    always_ff@(posedge i_clk) begin
        if(i_rst) begin
            axis_tx_data  <= 0;
            axis_tx_last  <= 0;
            axis_tx_valid <= 0;
        end 
        else begin
            axis_tx_data  <= s_axis_tdata ;
            axis_tx_last  <= s_axis_tlast ;
            axis_tx_valid <= s_axis_tvalid;
        end
    end

    always_ff@(posedge i_clk) begin
        if(i_rst) 
            axis_tx_ready <= 0;
        else if(s_axis_tlast)
            axis_tx_ready <= 0;
        else if(fifo_empty & !ip_axis_tx_valid & m_axis_tready)
            axis_tx_ready <= 1;
        else  
            axis_tx_ready <= axis_tx_ready;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            tx_handshake  <= 0;
        else 
            tx_handshake  <= s_axis_tvalid & s_axis_tready;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) begin
            ip_pkg_type <= 0;
            ip_pkg_len <= 0;
            ip_tx_target_ip <= SOURCE_IP;
        end
        else if(s_axis_tvalid) begin
            ip_pkg_type <= s_axis_tuser[23:16];
            ip_pkg_len <= s_axis_tuser[15:0] + 20;//add ip head length
            ip_tx_target_ip <= s_axis_tuser[55:24];
        end
        else begin
            ip_pkg_type <= ip_pkg_type;
            ip_pkg_len <= ip_pkg_len;
            ip_tx_target_ip <= ip_tx_target_ip;
        end
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            lsm_cnt <= 0;
        else if(lsm_cnt == ip_pkg_len - 1)
            lsm_cnt <= 0;
        else if(!fifo_empty & m_axis_tready)
            lsm_cnt <= lsm_cnt + 1;
        else 
            lsm_cnt <= lsm_cnt;
    end
 
    always_ff @(posedge i_clk) begin
        if(i_rst) 
            ip_tx_id <= 'd0;
        else if(ip_axis_tx_last)
            ip_tx_id <= ip_tx_id + 1;
        else 
            ip_tx_id <= ip_tx_id;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            fifo_rden <= 0;
        else if(lsm_cnt >= 19 && lsm_cnt < ip_pkg_len - 1 )
            fifo_rden <= 1;
        else 
            fifo_rden <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            fifo_empty_delay <= 0;
        else 
            fifo_empty_delay <= fifo_empty;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            check_sum <= 'd0;
        else if(lsm_cnt == 1)
            check_sum <= 16'h4500 + ip_pkg_len ;
        else if(lsm_cnt == 2)
            check_sum <= check_sum + ip_tx_id;
        else if(lsm_cnt == 3)
            check_sum <= check_sum + {{3'b010,5'd0},8'd0};
        else if(lsm_cnt == 4)
            check_sum <= check_sum + {8'd64,ip_pkg_type};
        else if(lsm_cnt == 5)
            check_sum <= check_sum + set_source_ip[31:16];
        else if(lsm_cnt == 6)
            check_sum <= check_sum + set_source_ip[15:0];
        else if(lsm_cnt == 7 )
            check_sum <= check_sum + ip_tx_target_ip[31:16] + ip_tx_target_ip[15:0];
        else if(lsm_cnt == 8)
            check_sum <= check_sum[31:16] + check_sum[15:0];
        else if(lsm_cnt == 9)
            check_sum <= ~check_sum;
        else    
            check_sum <= check_sum;
    end

    sfifo_8x64 sfifo_8x64_inst0 (
        .clk            ( i_clk             ),
        .srst           ( i_rst             ),
        .din            ( axis_tx_data      ),
        .wr_en          ( tx_handshake      ),
        .rd_en          ( fifo_rden         ),
        .dout           ( fifo_rddata       ),
        .almost_full    ( fifo_almost_full  ),
        .full           ( fifo_full         ),
        .empty          ( fifo_empty        ) 
    );

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            ip_tx_data <= 0;
        else case(lsm_cnt)
            0           :ip_tx_data <= {4'd4,4'd5};//ipv4 headlenght = 20
            1           :ip_tx_data <= 8'd0;//servce type
            2           :ip_tx_data <= ip_pkg_len[15:8];//len
            3           :ip_tx_data <= ip_pkg_len[7 :0];//len
            4           :ip_tx_data <= ip_tx_id[15:8];//counter
            5           :ip_tx_data <= ip_tx_id[7 :0];//counter
            6           :ip_tx_data <= {3'b010,5'd0};//cut information,dont
            7           :ip_tx_data <= 8'd0;//cut offset
            8           :ip_tx_data <= 8'd64;
            9           :ip_tx_data <= ip_pkg_type;
            10          :ip_tx_data <= check_sum[15:8];
            11          :ip_tx_data <= check_sum[7 :0];
            12          :ip_tx_data <= set_source_ip[31:24];
            13          :ip_tx_data <= set_source_ip[23:16];
            14          :ip_tx_data <= set_source_ip[15: 8];
            15          :ip_tx_data <= set_source_ip[7 : 0];
            16          :ip_tx_data <= ip_tx_target_ip[31:24];
            17          :ip_tx_data <= ip_tx_target_ip[23:16];
            18          :ip_tx_data <= ip_tx_target_ip[15: 8];
            19          :ip_tx_data <= ip_tx_target_ip[7 : 0];
            default     :ip_tx_data <= fifo_rddata;
        endcase
    end
    
    always_ff @(posedge i_clk)begin
        if(i_rst) 
            ip_axis_tx_user <= 0;
        else if(!fifo_empty & m_axis_tready & fifo_empty_delay)
            ip_axis_tx_user <= {1'b0,ip_tx_target_ip,16'h0800,ip_pkg_len};
        else 
            ip_axis_tx_user <= ip_axis_tx_user;
    end
    
    always_ff @(posedge i_clk) begin
        if(i_rst) 
            ip_axis_tx_last <= 0;
        else if(lsm_cnt == ip_pkg_len - 1)
            ip_axis_tx_last <= 1;
        else 
            ip_axis_tx_last <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            ip_axis_tx_valid <= 0;
        else if(ip_axis_tx_valid & ip_axis_tx_last)
            ip_axis_tx_valid <= 0;
        else if(!fifo_empty & m_axis_tready)
            ip_axis_tx_valid <= 1;
        else 
            ip_axis_tx_valid <= ip_axis_tx_valid;
    end


endmodule
